package core

import (
	"errors"
	"fmt"
	"io"
	"net"
	"time"
)

type TcpClient struct {
	conn            net.Conn
	onEvent         ClientEvent
	autoCon         bool
	autoConnectChan chan int
	obj             any
	lastError       error
	raw             bool //原始的数据，未加工，默认值为false
}

func NewTcpClient() *TcpClient {
	return &TcpClient{autoConnectChan: make(chan int, 100), autoCon: true, raw: false}
}
func (c *TcpClient) RawStream(res bool) *TcpClient {
	c.raw = res
	return c
}
func (c *TcpClient) GetLastError() error {
	return c.lastError
}
func (c *TcpClient) OnEvent(obj any, event ClientEvent) *TcpClient {
	c.onEvent = event
	c.obj = obj
	return c
}

// 发送重连事件，默认自动重连
func (c *TcpClient) postReConnectEvent() {
	go func() {
		if c.autoCon {
			time.Sleep(3 * time.Second)
			c.autoConnectChan <- 1
		}
	}()
}

func (c *TcpClient) connectServer(address string) error {
	conn, err := net.Dial("tcp", address)
	if err != nil {
		c.lastError = err
		if c.onEvent != nil {
			c.onEvent(c.obj, nil, Client_Error, nil)
		}
		c.postReConnectEvent()
		return err
	} else {
		c.conn = conn
		if tcpConn, ok := conn.(*net.TCPConn); ok {
			tcpConn.SetNoDelay(true)
			tcpConn.SetReadBuffer(1024 * 32)
			tcpConn.SetWriteBuffer(1024 * 32)            //视频流媒体服务 256K,IM 32K(保持10万并发)
			tcpConn.SetKeepAlive(true)                   // 保持长连接活跃
			tcpConn.SetKeepAlivePeriod(10 * time.Minute) // 减少保活探测频率
		}
	}
	return nil
}

// 释放客户端
func (c *TcpClient) Release() {
	c.autoCon = false
	c.close()
	close(c.autoConnectChan)
}

// 拒绝自动重新连接。
func (c *TcpClient) DisableAutoConnect() *TcpClient {
	c.autoCon = false
	return c
}

// 支持断开后自动重连
func (c *TcpClient) Connect(addr string) {
	go func() {
		for {
			if c.connectServer(addr) == nil {
				c.loopReceive()
			}

			if !c.autoCon {
				break
			}

			data := <-c.autoConnectChan
			if data == 0 {
				break
			}
		}
	}()
}

func (c *TcpClient) close() error {
	if c.conn == nil {
		return fmt.Errorf("client not connected")
	}
	err := c.conn.Close()
	c.conn = nil
	return err
}

func (c *TcpClient) loopReceive() error {
	defer c.close()

	if c.conn == nil {
		return errors.New("client not connected")
	}

	if c.onEvent != nil {
		c.onEvent(c.obj, c.conn, Client_Connect, nil)
	}

	for {
		if c.raw {
			buf := make([]byte, 1460)
			length, err := c.conn.Read(buf)
			if err != nil {
				c.lastError = err
				if err == io.EOF {
					if c.onEvent != nil {
						c.onEvent(c.obj, c.conn, Client_DisConnect, nil)
					}
					c.postReConnectEvent()
				} else {
					if c.onEvent != nil {
						c.onEvent(c.obj, c.conn, Client_Error, nil)
						c.postReConnectEvent()
					}
				}
				return err
			}
			if c.onEvent != nil {
				c.onEvent(c.obj, c.conn, Client_Data, buf[0:length])
			}
		} else {
			data, _, err := ReadPackage(c.conn)
			if err != nil {
				c.lastError = err
				if err == io.EOF {
					if c.onEvent != nil {
						c.onEvent(c.obj, c.conn, Client_DisConnect, nil)
					}
					c.postReConnectEvent()
				} else {
					if c.onEvent != nil {
						c.onEvent(c.obj, c.conn, Client_Error, nil)
						c.postReConnectEvent()
					}
				}
				return err
			}
			if c.onEvent != nil {
				c.onEvent(c.obj, c.conn, Client_Data, data)
			}
		}
	}
}
